package promote.brozen.netty.echo;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.util.ByteProcessor;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author Brozen
 * @date 2020/9/27 4:08 PM
 * @email brozen@qq.com
 */
public class NettyEchoSINWriter extends ChannelInboundHandlerAdapter {

    private AtomicBoolean exit = new AtomicBoolean(false);
    private ByteBuf buffer = PooledByteBufAllocator.DEFAULT.heapBuffer();

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 真实业务里，不能向ctx中提交一个死循环任务，ctx线程中会先执行这个task，然后继续处理同一个channel的读写，
        // 提交了死循环任务后，这个channel将永远得不到读写机会
        ctx.executor().scheduleAtFixedRate(() -> readAndSend(ctx), 0, 50, TimeUnit.MILLISECONDS);
        /*Thread thread = new Thread(() -> readAndSend(ctx));
        thread.setName("While-true-loop");
        thread.start();*/
    }


    private void readAndSend(ChannelHandlerContext ctx) {
        if (!exit.get()) {
            try {
                if (System.in.available() > 0) {
                    byte[] availableData = new byte[System.in.available()];
                    System.in.read(availableData);
                    buffer.writeBytes(availableData);
                } else {
                    return;
                }

                int idx = buffer.forEachByte(ByteProcessor.FIND_CRLF);
                int len = idx - buffer.readerIndex();

                // 增加引用，readSlice不会增加引用，但是write之后会释放引用，会导致buffer被free
                buffer.retain();
                ctx.writeAndFlush(buffer.readSlice(len));

                // 略过换行符，readSlice没有把换行符发送过去
                buffer.readByte();

                // 丢弃读过的数据，防止申请空间
                buffer.discardReadBytes();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("客户端发送异常");
        cause.printStackTrace();
    }
}
